﻿// <copyright file="TransieverInfo.cs" company="Hallvard Korsgaard">
//  Microsoft Public License (Ms-PL). See license details here: http://enoceannet.codeplex.com/license
// </copyright>
// <author>Hallvard Korsgaard</author>

namespace StcEthernet.Management
{
  using System;
  using System.Net;
  using System.Net.NetworkInformation;
  using System.Text;

  /// <summary>
  /// This class represents the configuration of the STC-Ethernet transiever.
  /// </summary>
  [Serializable]
  public class TransieverInfo
  {
    /// <summary>
    /// Initializes a new instance of the TransieverInfo class. 
    /// </summary>
    /// <param name="message">The raw data sent from the transiever.</param>
    internal TransieverInfo(byte[] message)
    {
      byte[] mac;
      byte[] ip;
      byte[] mask;
      byte[] gw;

      if (message == null)
      {
        throw new ArgumentNullException("message");
      }

      if (message.Length != 50)
      {
        throw new ArgumentOutOfRangeException("message", "length must be 50");
      }

      if (!((message[0] == 0x49 && message[1] == 0x4D && message[2] == 0x49 && message[3] == 0x4E) || (message[0] == 0x53 && message[1] == 0x45 && message[2] == 0x54 && message[3] == 0x43)))
      {
        throw new ArgumentException("Unkown preamble. Expected IMIN or SETC.", "message");
      }

      mac = new byte[6];
      Array.Copy(message, 4, mac, 0, 6);
      this.PhysicalAddress = new PhysicalAddress(mac);

      this.ClientServerMode = (ClientServerMode)message[10];

      ip = new byte[4];
      Array.Copy(message, 11, ip, 0, 4);
      this.IPAddress = new IPAddress(ip);

      mask = new byte[4];
      Array.Copy(message, 15, mask, 0, 4);
      this.SubnetMask = new IPAddress(mask);

      gw = new byte[4];
      Array.Copy(message, 19, gw, 0, 4);
      this.DefaultGateway = new IPAddress(gw);

      this.TransieverPort = this.Convert16bit(message[23], message[24]);

      ip = new byte[4];
      Array.Copy(message, 25, ip, 0, 4);
      this.PeerIPAddress = new IPAddress(ip);

      this.PeerPort = this.Convert16bit(message[29], message[30]);

      this.Speed = message[31];
      this.Databit = message[32];
      this.Parity = message[33];
      this.StopBit = message[34];
      this.Flow = message[35];
      this.DChar = message[36];
      this.DSize = this.Convert16bit(message[37], message[38]);
      this.DTime = this.Convert16bit(message[39], message[40]);
      this.ITime = this.Convert16bit(message[41], message[42]);
      this.DebugOff = message[43];
      this.MajorVersion = message[44];
      this.MinorVersion = message[45];
      this.UseDhcp = message[46] == 1;
      this.UseUdp = message[47] == 1;
      this.Connect = message[48];
    }

    /// <summary>
    /// Gets the MAC address.
    /// </summary>
    public PhysicalAddress PhysicalAddress
    {
      get;
      internal set;
    }

    /// <summary>
    /// Gets or sets the client/server mode.
    /// </summary>
    public ClientServerMode ClientServerMode
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the ip address.
    /// </summary>
    public IPAddress IPAddress
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the ip subnet mask.
    /// </summary>
    public IPAddress SubnetMask
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the default gateway.
    /// </summary>
    public IPAddress DefaultGateway
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the transiever port.
    /// </summary>
    public int TransieverPort
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the ip address of the server.
    /// </summary>
    public IPAddress PeerIPAddress
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the server port.
    /// </summary>
    public int PeerPort
    {
      get;
      set;
    }

    /// <summary>
    /// Gets the MajorVersion.
    /// </summary>
    public byte MajorVersion
    {
      get;
      internal set;
    }

    /// <summary>
    /// Gets the MinorVersion.
    /// </summary>
    public byte MinorVersion
    {
      get;
      internal set;
    }

    /// <summary>
    /// Gets or sets a value indicating whether to use DHCP mode or not.
    /// </summary>
    public bool UseDhcp
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets a value indicating whether to use UDP mode or not.
    /// </summary>
    public bool UseUdp
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the speed.
    /// </summary>
    internal byte Speed
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the databit.
    /// </summary>
    internal byte Databit
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the parity.
    /// </summary>
    internal byte Parity
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the flow.
    /// </summary>
    internal byte Flow
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the stop bit.
    /// </summary>
    internal byte StopBit
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the DChar.
    /// </summary>
    internal byte DChar
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the DSize.
    /// </summary>
    internal int DSize
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the DTime.
    /// </summary>
    internal int DTime
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the ITime.
    /// </summary>
    internal int ITime
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the DebugOff.
    /// </summary>
    internal byte DebugOff
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets connect.
    /// </summary>
    internal byte Connect
    {
      get;
      set;
    }

    /// <summary>
    /// Returns a string that represents the current object.
    /// </summary>
    /// <returns>A string that represents the current object.</returns>
    public override string ToString()
    {
      StringBuilder sb;

      sb = new StringBuilder();
      sb.Append("Physical address: ").AppendLine(this.PhysicalAddress.ToString());
      sb.Append("ClientServerMode: ").AppendLine(this.ClientServerMode.ToString());
      sb.Append("IP address      : ").AppendLine(this.IPAddress.ToString());
      sb.Append("Subnet mask     : ").AppendLine(this.SubnetMask.ToString());
      sb.Append("Default gateway : ").AppendLine(this.DefaultGateway.ToString());
      sb.Append("Transiever port : ").AppendLine(this.TransieverPort.ToString());
      sb.Append("Peer IP address : ").AppendLine(this.PeerIPAddress.ToString());
      sb.Append("Peer port       : ").AppendLine(this.PeerPort.ToString());
      /*
      sb.Append("Speed           : ").AppendLine(this.Speed.ToString());
      sb.Append("Databit         : ").AppendLine(this.Databit.ToString());
      sb.Append("Parity          : ").AppendLine(this.Parity.ToString());
      sb.Append("Stopbit         : ").AppendLine(this.StopBit.ToString());
      sb.Append("Flow            : ").AppendLine(this.Flow.ToString());
      sb.Append("DChar           : ").AppendLine(this.DChar.ToString());
      sb.Append("DSize           : ").AppendLine(this.DSize.ToString());
      sb.Append("DTime           : ").AppendLine(this.DTime.ToString());
      sb.Append("ITime           : ").AppendLine(this.ITime.ToString());
      sb.Append("DebugOff        : ").AppendLine(this.DebugOff.ToString());
      */
      sb.Append("Major version   : ").AppendLine(this.MajorVersion.ToString());
      sb.Append("Minor version   : ").AppendLine(this.MinorVersion.ToString());
      sb.Append("DHCP            : ").AppendLine(this.UseDhcp.ToString());
      sb.Append("UDP             : ").AppendLine(this.UseUdp.ToString());
      
      // sb.Append("Connect         : ").AppendLine(this.Connect.ToString());
      return sb.ToString();
    }

    /// <summary>
    /// Returns the SETT message used for updating the transiever.
    /// </summary>
    /// <returns>The SETT message used for updating the transiever.</returns>
    internal byte[] GetBytes()
    {
      byte[] message;

      message = new byte[49];
      message[0] = 0x53;
      message[1] = 0x45;
      message[2] = 0x54;
      message[3] = 0x54;
      Array.Copy(this.PhysicalAddress.GetAddressBytes(), 0, message, 4, 6);
      message[10] = (byte)this.ClientServerMode;
      Array.Copy(this.IPAddress.GetAddressBytes(), 0, message, 11, 4);
      Array.Copy(this.SubnetMask.GetAddressBytes(), 0, message, 15, 4);
      Array.Copy(this.DefaultGateway.GetAddressBytes(), 0, message, 19, 4);
      message[23] = (byte)(this.TransieverPort >> 8);
      message[24] = (byte)(this.TransieverPort & 0xff);
      Array.Copy(this.PeerIPAddress.GetAddressBytes(), 0, message, 25, 4);
      message[29] = (byte)(this.PeerPort >> 8);
      message[30] = (byte)(this.PeerPort & 0xff);
      message[31] = this.Speed;
      message[32] = this.Databit;
      message[33] = this.Parity;
      message[34] = this.StopBit;
      message[35] = this.Flow;
      message[36] = this.DChar;
      message[37] = (byte)(this.DSize >> 8);
      message[38] = (byte)(this.DSize & 0xff);
      message[39] = (byte)(this.DTime >> 8);
      message[40] = (byte)(this.DTime & 0xff);
      message[41] = (byte)(this.ITime >> 8);
      message[42] = (byte)(this.ITime & 0xff);
      message[43] = this.DebugOff;
      message[44] = this.MajorVersion;
      message[45] = this.MinorVersion;
      message[46] = this.UseDhcp ? (byte)1 : (byte)0;
      message[47] = this.UseUdp ? (byte)1 : (byte)0;
      message[48] = this.Connect;

      return message;
    }

    /// <summary>
    /// Returns a 16 value constructed from two bytes.
    /// </summary>
    /// <param name="b1">The MSB byte.</param>
    /// <param name="b0">The LSB byte.</param>
    /// <returns>A 16 value.</returns>
    private int Convert16bit(byte b1, byte b0)
    {
      int id;

      id = b1;
      id <<= 8;

      id |= b0;

      return id;
    }
  }
}
